﻿#include "businessmsghandler.h"

#include "businessnetwork.h"
#include "AppConfig/appconfig.h"

#include "protocol/protocolConverter.h"
#include "businessuser.h"
#include <CipherUtils/cipherutils.h>

#include <string>
#include <ThreadPool/threadpool.h>
#include "businesssql.h"
#include <IdGenerator/idgenerator.h>
#include <FileManager/filemanager.h>
#include <SyncManager/syncmanager.h>
#include <Debug/DEBUG.hpp>
#include <filesystem>
#include "businessfile.h"
#include "businessgroup.h"
#include <protocol/protocolBuilder.h>
#include <codecvt>

void BusinessMsgHandler::init()
{

    // register call back function

    msgRouter.registerCallback("7|1", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessMsgHandler::getInstance().loginServer(protocol, channel);
    });

    msgRouter.registerCallback("1|1",[](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessMsgHandler::getInstance().chatToUser(protocol, channel);
    });

    msgRouter.registerCallback("1|3",[](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessMsgHandler::getInstance().queryOffineMessage(protocol, channel);
    });
    msgRouter.registerCallback("4|1",[](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessMsgHandler::getInstance().returnUniqueID(protocol, channel);
    });
    msgRouter.registerCallback("3|1",[](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessFile::getInstance().recvUploadFileRequest(protocol, channel);
    });
    msgRouter.registerCallback("3|3",[](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessFile::getInstance().recvFileBlock(protocol, channel);
    });

    msgRouter.registerCallback("3|4",[](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessFile::getInstance().recvEndFileTransmission(protocol, channel);
    });

    msgRouter.registerCallback("3|2",[](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessMsgHandler::getInstance().recvDownloadRequest(protocol, channel);
    });
    msgRouter.registerCallback("4|3", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessMsgHandler::getInstance().RecvReplyOfApply(protocol, channel);
    });
    msgRouter.registerCallback("4|4", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessMsgHandler::getInstance().RecvReplyOfApply(protocol, channel);
    });
    msgRouter.registerCallback("5|2", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessFile::getInstance().RecvConfirmOfFileBlock(protocol, channel);
    });
    msgRouter.registerCallback("3|7", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessFile::getInstance().sendFileList(protocol, channel);
    });
    msgRouter.registerCallback("2|7", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessUser::getInstance().recvReplyOfFriendApply(protocol, channel);
    });
    msgRouter.registerCallback("2|8", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessUser::getInstance().recvReplyOfFriendApply(protocol, channel);
    });
    msgRouter.registerCallback("2|1", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessUser::getInstance().recvApplyOfFriend(protocol, channel);
    });
    msgRouter.registerCallback("2|9", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessUser::getInstance().refreshUserList(protocol, channel);
    });
    msgRouter.registerCallback("2|10", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessUser::getInstance().ReplyGetTargetUser(protocol, channel);
    });
    msgRouter.registerCallback("2|3", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessUser::getInstance().eraseFriend(protocol, channel);
    });
    msgRouter.registerCallback("2|12", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessGroup::getInstance().recvCreateGroupRequest(protocol, channel);
    });
    msgRouter.registerCallback("2|13", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessGroup::getInstance().recvUpdateGroupList(protocol, channel);
    });
    msgRouter.registerCallback("1|2", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessGroup::getInstance().recvGroupMessage(protocol, channel);
    });
    msgRouter.registerCallback("2|2", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessGroup::getInstance().recvJoinGroupRequest(protocol, channel);
    });
    msgRouter.registerCallback("2|4", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessGroup::getInstance().recvLeaveGroup(protocol, channel);
    });
    msgRouter.registerCallback("2|14", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessGroup::getInstance().recvReplyOfGroupApply(protocol, channel);
    });
    msgRouter.registerCallback("2|15", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessGroup::getInstance().recvReplyOfGroupApply(protocol, channel);
    });
    msgRouter.registerCallback("2|16", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessGroup::getInstance().recvUpdateGroupRequestList(protocol, channel);
    });
    msgRouter.registerCallback("2|17", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessUser::getInstance().recvUpdateAvatar(protocol, channel);
    });
}

void BusinessMsgHandler::handleMsg(std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel)
{
    ProtocolConverter converter;
    converter.setProtocolOption(BusinessNetWork::getInstance().getProtocolOption());
    converter.parseData((unsigned char*)protocol->data());

    int type = std::get<int64_t>(converter.getHeaderField(0));
    int subType = std::get<int64_t>(converter.getHeaderField(1));

    // decrypt message
    AppConfig& config = AppConfig::getInstance();
    int mode = std::stoi(config.globalValue("TransmissionMode"));
    if (mode == 2 && (type != 7 || subType != 1)) {
        // get user aesKey and iv
        uint64_t id = BusinessNetWork::getInstance().getUserID(channel);

        User& user = BusinessUser::getInstance().getTargetUser(id);

        std::string AESKey = user["RemoteAESKey"];
        std::string IV = user["RemoteAESIV"];

        CipherUtils& utils = CipherUtils::getInstance();

        std::string cipherText;
        cipherText.resize(converter.getParsedDataSize());
        converter.copyParsedData(cipherText.data());

        auto res = utils.AES_Decrypt((unsigned char*)cipherText.data(), cipherText.size(),
                                     (unsigned char*)AESKey.data(),
                                     (unsigned char*)IV.data());

        ProtocolBuilder builder;
        builder.setProtocolOption(BusinessNetWork::getInstance().getProtocolOption());
        builder.updateProtocolData(protocol, res.first.get(), res.second);
    }

    std::string commandStr = std::to_string(type) + "|" + std::to_string(subType);
    DEBUG_INFO("receiver commandStr" + commandStr);
    // exec target function
    msgRouter[commandStr](protocol, channel);
}

void BusinessMsgHandler::chatToUser(std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr &channel)
{
    ProtocolConverter converter;
    converter.setProtocolOption(BusinessNetWork::getInstance().getProtocolOption());
    converter.parseData((unsigned char*)protocol->data());

    UserManager& manager = UserManager::getInstance();
    BusinessNetWork& net = BusinessNetWork::getInstance();
    //get source user
    auto id1 = std::get<int64_t>(converter.getHeaderField(2));
    nlohmann::json sourceUser = manager.getTargetUser(id1);
    //get target user
    auto id2 = std::get<int64_t>(converter.getHeaderField(3));
    // send message to id2 user
    nlohmann::json messageJson = getJsonOfMessage(protocol);

    std::string messageStr = messageJson["Message"];
    int messageType = messageJson["MessageType"];

    BusinessSQL& sql = BusinessSQL::getInstance();
    std::pair<uint64_t, std::string> messageInfo;
    bool targetIsOnline = manager.queryUserOnline(id2);

    if (!targetIsOnline) {
        // if target user is offline => restore message to database
        messageInfo = sql.insertUserMessage(id1, id2, messageStr, "pending", messageType);
    } else {
        messageInfo = sql.insertUserMessage(id1, id2, messageStr, "sended", messageType);
    }

    nlohmann::json messagejson;
    messagejson["Message"] = messageStr;
    messagejson["MessageID"] = messageInfo.first;
    messagejson["Time"] = messageInfo.second;
    messagejson["MessageType"] = messageType;
    std::string sendStr = messagejson.dump();

    if (targetIsOnline) {
        //get target channel
        auto targetChannel = net.getChannel(id2);
        BusinessNetWork::getInstance().sendMessageToClient(targetChannel,
                                                           1,1,id1, id2, 0,
                                                           (unsigned char*)sendStr.data(), sendStr.size());
    }
    BusinessNetWork::getInstance().sendMessageToClient(channel,
                                                       1,1,id1, id2, 0,
                                                       (unsigned char*)sendStr.data(), sendStr.size());
}

BusinessMsgHandler::BusinessMsgHandler() {}

void BusinessMsgHandler::RecvReplyOfApply(std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel) {
    ProtocolConverter converter;
    converter.setProtocolOption(BusinessNetWork::getInstance().getProtocolOption());
    converter.parseData((unsigned char *)protocol->data());
    uint64_t id3 = std::get<int64_t>(converter.getHeaderField(4));
    int subType = std::get<int64_t>(converter.getHeaderField(1));

    SyncManager& manager = SyncManager::getInstance();
    DEBUG_INFO("receive 4|x, x = " + std::to_string(subType));
    if (subType == 3) {
        // accept the apply
        manager.releaseSyncPoint(id3, true);
    } else {
        // reject the apply
        manager.releaseSyncPoint(id3, false);
    }
}

void BusinessMsgHandler::loginServer(std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel)
{
    ProtocolConverter converter;
    converter.setProtocolOption(BusinessNetWork::getInstance().getProtocolOption());
    converter.parseData((unsigned char*)protocol->data());
    nlohmann::json message = nlohmann::json::parse(decryptMessageOfProtocol(protocol, nullptr, true));
    std::string account = message["account"];
    std::string password = message["password"];
    std::string client_ECC_public = message["ECC_public"];

    DEBUG_INFO(client_ECC_public);

    BusinessSQL& sql = BusinessSQL::getInstance();

    if (BusinessUser::getInstance().varifyUser(account, password)) {
        // user from database
        nlohmann::json userDB = sql.queryAUser(account);
        ID id = userDB["UserID"];
        // 检查当前用户是否已经登录
        if (BusinessNetWork::getInstance().checkTargetUserOnline(id)) {
            // 当前的用户已经登录了，拒绝这个连接
            // false
            // refuse
            BusinessNetWork::getInstance().sendMessageToClient(channel,
                                                               4, 4, 0, 0,
                                                               std::get<int64_t>(converter.getHeaderField(4)),
                                                               nullptr, 0, true);
            return ;
        }
        // true
        // login in success
        // 1. reply apply
        // 2. init the user
        SyncPoint point = std::get<int64_t>(converter.getHeaderField(4));
        BusinessNetWork::getInstance().sendMessageToClient(channel,
                                                           4, 3, 0, 0,
                                                           point,
                                                           nullptr, 0, true);

        UserManager& manager = UserManager::getInstance();
        AppConfig& config = AppConfig::getInstance();

        // add a user in user manager

        User& user = manager.createNewUser(id, account);
        user["ECC_public"] = client_ECC_public;
        CipherUtils& utils = CipherUtils::getInstance();
        auto ECDHKey = utils.ECDH_computeKey(config.globalValue("ECC_private"), client_ECC_public);
        auto AESandIV = utils.AES_generateFromECDHSharedKey(ECDHKey.first, ECDHKey.second);

        std::string AESkey, AESIV;

        AESkey.resize(256/8);
        AESIV.resize(128/8);

        memcpy(AESkey.data(), AESandIV[0].get(), 256/8);
        memcpy(AESIV.data(), AESandIV[1].get(), 128/8);

        user["RemoteAESKey"] = AESkey;
        user["RemoteAESIV"] = AESIV;


        // maintain the channel of user;
        auto userInfo = BusinessUser::getInstance().getTargetUser(account);
        BusinessNetWork::getInstance().addSocketChannel(userInfo["UserID"], channel);
        BusinessUser::getInstance().newLoginUser(userInfo["UserID"], channel);

        // init the client when login the server success
        initClientAfterLogin(account, channel);
    } else {
        // false
        // refuse
        BusinessNetWork::getInstance().sendMessageToClient(channel,
                                                           4, 4, 0, 0,
                                                           std::get<int64_t>(converter.getHeaderField(4)),
                                                           nullptr, 0, true);

    }

}

void BusinessMsgHandler::initClientAfterLogin(const std::string &account, const hv::SocketChannelPtr &channel)
{
    // 1. query all the friend of user
    // 2. build into a protocol
    BusinessSQL& sql = BusinessSQL::getInstance();
    nlohmann::json userMessage = sql.queryAUser(account);
    DEBUG_INFO("init client after login: " + userMessage.dump());
    nlohmann::json friendMessage = sql.queryAllTheUserOfTargetRelation(account);
    DEBUG_INFO("init client after login: " + friendMessage.dump());
    // get all the pending user
    nlohmann::json pendingFriend = sql.queryAllTheUserOfTargetRelation(account, "pending");
    DEBUG_INFO("init client after login: " + pendingFriend.dump());
    nlohmann::json message;

    message["User"] = userMessage;
    message["friendMessage"] = friendMessage;
    message["pendingMessage"] = pendingFriend;

    std::string messageStr = message.dump();

    BusinessNetWork& net = BusinessNetWork::getInstance();

    net.sendMessageToClient(channel,
                            6, 2, 0, 0, 0,
                            (unsigned char*)messageStr.c_str(), messageStr.size());

    // refresh file list
    BusinessFile::getInstance().sendFileList(nullptr, channel);
    BusinessGroup::getInstance().updateGroupList(account, channel);
}

std::string BusinessMsgHandler::decryptMessageOfProtocol(std::shared_ptr<Protocol> protocol, User* user, bool doNotDecrypt)
{
    std::string res;
    AppConfig& config = AppConfig::getInstance();

    ProtocolConverter converter;
    converter.setProtocolOption(BusinessNetWork::getInstance().getProtocolOption());
    converter.parseData((unsigned char*)protocol->data());

    int mode = std::stoi(config.globalValue("TransmissionMode"));

    res.resize(converter.getParsedDataSize(), '\0');
    converter.copyParsedData(res.data());
    if ((mode == 1) || doNotDecrypt) {
        // do nothing
    } else {
        std::string AESKey = (*user)["RemoteAESKey"];
        std::string AESIV = (*user)["RemoteAESIV"];
        CipherUtils& utils = CipherUtils::getInstance();
        auto text = utils.AES_Decrypt((unsigned char*)res.data(), converter.getParsedDataSize(), (unsigned char*)AESKey.data(), (unsigned char*)AESIV.data());
        memcpy(res.data(), text.first.get(), text.second);
        res.resize(text.second + 1);
        res[text.second] = '\0';
    }
    return res;
}

void BusinessMsgHandler::queryOffineMessage(std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr &channel)
{
    BusinessSQL & sql = BusinessSQL::getInstance();

    ProtocolConverter converter;
    converter.setProtocolOption(BusinessNetWork::getInstance().getProtocolOption());
    converter.parseData((unsigned char*)protocol->data());

    uint64_t receiverID = std::get<int64_t>(converter.getHeaderField(2));
    uint64_t senderID = std::get<int64_t>(converter.getHeaderField(3));

    nlohmann::json messages = sql.queryOffineMessage(senderID, receiverID);
    for (auto& message : messages) {
        // send every offline message to user
        int messageID = message["MessageID"];

        std::string messageStr = message.dump();
        BusinessNetWork::getInstance().sendMessageToClient(channel,
                                                           1,4,0, receiverID, senderID,
                                                           (unsigned char *)messageStr.c_str(), messageStr.size());
        BusinessSQL::getInstance().updateOfflineMessageStatus(messageID);
    }

}

void BusinessMsgHandler::returnUniqueID(std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr &channel)
{
    AppConfig& config = AppConfig::getInstance();
    IDGenerator generator(std::stoi(config.globalValue("MechineID")));
    uint64_t id = generator.generate();
    DEBUG_INFO("generate unique id  : " + std::to_string(id));
    ProtocolConverter converter;
    converter.setProtocolOption(BusinessNetWork::getInstance().getProtocolOption());
    converter.parseData((unsigned char*)protocol->data());

    uint64_t syncPoint = std::get<int64_t>(converter.getHeaderField(4));

    BusinessNetWork::getInstance().sendMessageToClient(channel,
                                                       4, 2, id,0, syncPoint, nullptr, 0);
}

void BusinessMsgHandler::recvDownloadRequest(std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr &channel)
{
    ThreadPool& pool = ThreadPool::getInstance();
    pool.commit([&](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr &channel){
        BusinessFile::getInstance().recvDownloadRequest(protocol, channel);
    },protocol, channel);
}

nlohmann::json BusinessMsgHandler::getJsonOfMessage(std::shared_ptr<Protocol> protocol)
{
    ProtocolConverter converter;
    converter.setProtocolOption(BusinessNetWork::getInstance().getProtocolOption());
    converter.parseData((unsigned char*)protocol->data());
    std::string jsonStr;
    jsonStr.resize(converter.getParsedDataSize(), '\0');
    converter.copyParsedData(jsonStr.data());
    return nlohmann::json::parse(jsonStr);
}
